Ext.define('Ext.ux.MonthPicker', {
   extend: 'Ext.form.field.Date',
   alias: 'widget.monthpicker',
   requires: ['Ext.picker.Month'],
   alternateClassName: ['Ext.form.MonthField', 'Ext.form.Month'],
   selectMonth: null,
   createPicker: function() {
       var me = this,
           format = Ext.String.format;
       return Ext.create('Ext.picker.Month', {
           pickerField: me,
           ownerCt: me.ownerCt,
           renderTo: document.body,
           floating: true,
           hidden: true,
           focusOnShow: true,
           minDate: me.minValue,
           maxDate: me.maxValue,
           disabledDatesRE: me.disabledDatesRE,
           disabledDatesText: me.disabledDatesText,
           disabledDays: me.disabledDays,
           disabledDaysText: me.disabledDaysText,
           format: me.format,
           showToday: me.showToday,
           startDay: me.startDay,
           minText: format(me.minText, me.formatDate(me.minValue)),
           maxText: format(me.maxText, me.formatDate(me.maxValue)),
           listeners: {
               select: {
                   scope: me,
                   fn: me.onSelect
               },
               monthdblclick: {
                   scope: me,
                   fn: me.onOKClick
               },
               yeardblclick: {
                   scope: me,
                   fn: me.onOKClick
               },
               OkClick: {
                   scope: me,
                   fn: me.onOKClick
               },
               CancelClick: {
                   scope: me,
                   fn: me.onCancelClick
               },
               //注意：Extjs5 需要此语句，否则会出现选不中的BUG
               //start---------------
               afterrender : {
                   scope : me,
                   fn : function(c) {
                       var me = c;
                       me.el.on("mousedown", function(e) {
                           e.preventDefault();
                       }, c);
                   }
               },
               //-----------------end
           },
           keyNavConfig: {
               esc: function() {
                   me.collapse();
               }
           }
       });
   },
   onCancelClick: function() {
       var me = this;
       me.selectMonth = null;
       me.collapse();
   },
   onOKClick: function() {
       var me = this;
       if (me.selectMonth) {
           me.setValue(me.selectMonth);
           me.fireEvent('select', me, me.selectMonth);
       }
       me.collapse();
   },
   onSelect: function(m, d) {
       var me = this;
       me.selectMonth = new Date((d[0] + 1) + '/1/' + d[1]);
   }
});

//只选择年的控件
Ext.define('ux.picker.Year', {
   extend: 'Ext.Component',
   alias: 'widget.uxYearpicker',
   width:'auto',
   isYearPicker: true,

   focusable: true,

   childEls: [
       'bodyEl', 'prevEl', 'nextEl', 'yearEl'
   ],

   renderTpl: [
       '<div id="{id}-bodyEl" data-ref="bodyEl" class="{baseCls}-body">',
       '<div id="{id}-yearEl" data-ref="yearEl" class="{baseCls}-years">',
       '<div class="{baseCls}-yearnav">',
       '<div class="{baseCls}-yearnav-button-ct">',
       '<a id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-prev" hidefocus="on" role="button"></a>',
       '</div>',
       '<div class="{baseCls}-yearnav-button-ct">',
       '<a id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-next" hidefocus="on" role="button"></a>',
       '</div>',
       '</div>',
       '<tpl for="years">',
       '<div class="{parent.baseCls}-item {parent.baseCls}-year">',
       '<a hidefocus="on" class="{parent.baseCls}-item-inner" role="button">{.}</a>',
       '</div>',
       '</tpl>',
       '</div>',
       '<div class="' + Ext.baseCSSPrefix + 'clear"></div>',
       '<tpl if="showButtons">',
       '<div class="{baseCls}-buttons">{%',
       'var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;',
       'okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;',
       'okBtn.ownerCt = cancelBtn.ownerCt = me;',
       'Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);',
       'Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);',
       '%}</div>',
       '</tpl>',
       '</div>'
   ],

   //<locale>
   /**
    * @cfg {String} okText The text to display on the ok button.
    */
   okText: '确定',
   //</locale>

   //<locale>
   /**
    * @cfg {String} cancelText The text to display on the cancel button.
    */
   cancelText: '取消',
   //</locale>

   /**
    * @cfg {String} [baseCls='x-monthpicker']
    *  The base CSS class to apply to the picker element.
    */
   baseCls: Ext.baseCSSPrefix + 'monthpicker',

   /**
    * @cfg {Boolean} showButtons True to show ok and cancel buttons below the picker.
    */
   showButtons: true,

   /**
    * @cfg {String} [selectedCls='x-monthpicker-selected'] The class to be added to selected items in the picker.
    */

   /**
    * @cfg {Date/Number[]} value The default value to set. See {@link #setValue}
    */

   /**
    * @cfg {String}
    * The {@link Ext.button.Button#ui} to use for the month picker's footer buttons.
    */
   footerButtonUI: 'default',

   measureWidth: 35,
   measureMaxHeight: 20,

   // used when attached to date picker which isnt showing buttons
   smallCls: Ext.baseCSSPrefix + 'monthpicker-small',

   /**
    * @private
    */
   totalYears: 10,
   yearOffset: 5, // 10 years in total, 2 per row
   monthOffset: 6, // 12 months, 2 per row
   alignOnScroll: false,

   /**
    * @event cancelclick
    * Fires when the cancel button is pressed.
    * @param {Ext.picker.Month} this
    */

   /**
    * @event monthclick
    * Fires when a month is clicked.
    * @param {Ext.picker.Month} this
    * @param {Array} value The current value
    */

   /**
    * @event monthdblclick
    * Fires when a month is clicked.
    * @param {Ext.picker.Month} this
    * @param {Array} value The current value
    */

   /**
    * @event okclick
    * Fires when the ok button is pressed.
    * @param {Ext.picker.Month} this
    * @param {Array} value The current value
    */

   /**
    * @event select
    * Fires when a month/year is selected.
    * @param {Ext.picker.Month} this
    * @param {Array} value The current value
    */

   /**
    * @event yearclick
    * Fires when a year is clicked.
    * @param {Ext.picker.Month} this
    * @param {Array} value The current value
    */

   /**
    * @event yeardblclick
    * Fires when a year is clicked.
    * @param {Ext.picker.Month} this
    * @param {Array} value The current value
    */

   /**
    * @inheritdoc
    * @private
    */
   initComponent: function () {
       var me = this;

       me.selectedCls = me.baseCls + '-selected';

       if (me.small) {
           me.addCls(me.smallCls);
       }
       me.setValue(me.value);
       me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);

       if (me.showButtons) {
           me.okBtn = new Ext.button.Button({
               ui: me.footerButtonUI,
               text: me.okText,
               handler: me.onOkClick,
               scope: me
           });
           me.cancelBtn = new Ext.button.Button({
               ui: me.footerButtonUI,
               text: me.cancelText,
               handler: me.onCancelClick,
               scope: me
           });
       }

       this.callParent();
   },

   /**
    * @inheritdoc
    * @private
    */
   beforeRender: function () {
       var me = this;

       if (me.padding && !me.width) {
           me.cacheWidth();
       }

       me.callParent();

       Ext.apply(me.renderData, {
           years: me.getYears(),
           showButtons: me.showButtons
       });
   },

   cacheWidth: function () {
       var me = this,
           padding = me.parseBox(me.padding),
           widthEl = Ext.getBody().createChild({
               cls: me.baseCls + ' ' + me.borderBoxCls,
               style: 'position:absolute;top:-1000px;left:-1000px;',
               html: '&nbsp;' // required for opera 11.64 to measure a width
           });

       me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right;
       widthEl.destroy();
   },

   /**
    * @inheritdoc
    * @private
    */
   afterRender: function () {
       var me = this,
           body = me.bodyEl;

       me.callParent();

       me.el.on('mousedown', me.onElClick, me, {
           translate: false
       });

       body.on({
           scope: me,
           click: 'onBodyClick',
           dblclick: 'onBodyClick'
       });

       // keep a reference to the year/month elements since we'll be re-using them
       me.years = body.select('.' + me.baseCls + '-year a');

       me.backRepeater = new Ext.util.ClickRepeater(me.prevEl, {
           handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears])
       });

       me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over');
       me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {
           handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears])
       });
       me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over');
       me.updateBody();

   },

   /**
    * Set the value for the picker.
    * @param {Date/Number[]} value The value to set. It can be a Date object, where the month/year will be extracted, or
    * it can be an array, with the month as the first index and the year as the second.
    * @return {Ext.picker.Month} this
    */
   setValue: function (value) {
       var me = this,
           active = me.activeYear,
           year;

       if (!value) {
           me.value = [null, null];
       } else if (Ext.isDate(value)) {
           me.value = [value.getMonth(), value.getFullYear()];
       } else {
           me.value = [value[0], value[1]];
       }

       if (me.rendered) {
           year = me.value[1];
           if (year !== null) {
               if ((year < active || year > active + me.yearOffset)) {
                   me.activeYear = year - me.yearOffset + 1;
               }
           }
           me.updateBody();
       }

       return me;
   },

   /**
    * Gets the selected value. It is returned as an array [month, year]. It may
    * be a partial value, for example [null, 2010]. The month is returned as
    * 0 based.
    * @return {Number[]} The selected value
    */
   getValue: function () {
       return this.value;
   },

   /**
    * Checks whether the picker has a selection
    * @return {Boolean} Returns true if both a month and year have been selected
    */
   hasSelection: function () {
       var value = this.value;
       return value[0] !== null && value[1] !== null;
   },

   /**
    * Get an array of years to be pushed in the template. It is not in strict
    * numerical order because we want to show them in columns.
    * @private
    * @return {Number[]} An array of years
    */
   getYears: function () {
       var me = this,
           offset = me.yearOffset,
           start = me.activeYear, // put the "active" year on the left
           end = start + offset,
           i = start,
           years = [];

       for (; i < end; ++i) {
           years.push(i, i + offset);
       }

       return years;
   },

   /**
    * Update the years in the body based on any change
    * @private
    */
   updateBody: function () {
       var me = this,
           years = me.years,
           yearNumbers = me.getYears(),
           cls = me.selectedCls,
           value = me.getYear(null),
           year,
           yearItems, y, yLen, el;

       if (me.rendered) {
           years.removeCls(cls);

           yearItems = years.elements;
           yLen = yearItems.length;

           for (y = 0; y < yLen; y++) {
               el = Ext.fly(yearItems[y]);

               year = yearNumbers[y];
               el.dom.innerHTML = year;
               if (year === value) {
                   el.addCls(cls);
               }
           }
       }
   },

   /**
    * Gets the current year value, or the default.
    * @private
    * @param {Number} defaultValue The default value to use if the year is not defined.
    * @param {Number} offset A number to offset the value by
    * @return {Number} The year value
    */
   getYear: function (defaultValue, offset) {
       var year = this.value[1];
       offset = offset || 0;
       return year === null ? defaultValue : year + offset;
   },

   onElClick: function (e) {
       e.stopEvent();
   },

   /**
    * React to clicks on the body
    * @private
    */
   onBodyClick: function (e, t) {
       var me = this,
           isDouble = e.type === 'dblclick';

       if (e.getTarget('.' + me.baseCls + '-year')) {
           e.stopEvent();
           me.onYearClick(t, isDouble);
       }
   },

   /**
    * Modify the year display by passing an offset.
    * @param {Number} [offset=10] The offset to move by.
    */
   adjustYear: function (offset) {
       if (typeof offset !== 'number') {
           offset = this.totalYears;
       }
       this.activeYear += offset;
       this.updateBody();
   },

   /**
    * React to the ok button being pressed
    * @private
    */
   onOkClick: function () {
       this.fireEvent('okclick', this, this.value);
   },

   /**
    * React to the cancel button being pressed
    * @private
    */
   onCancelClick: function () {
       this.fireEvent('cancelclick', this);
   },

   /**
    * React to a year being clicked
    * @private
    * @param {HTMLElement} target The element that was clicked
    * @param {Boolean} isDouble True if the event was a doubleclick
    */
   onYearClick: function (target, isDouble) {
       var me = this;
       me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset);
       me.updateBody();
       me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
       me.fireEvent('select', me, me.value);

   },

   /**
    * Returns an offsetted number based on the position in the collection. Since our collections aren't
    * numerically ordered, this function helps to normalize those differences.
    * @private
    * @param {Object} index
    * @param {Object} offset
    * @return {Number} The correctly offsetted number
    */
   resolveOffset: function (index, offset) {
       if (index % 2 === 0) {
           return (index / 2);
       }
       return offset + Math.floor(index / 2);
   },

   /**
    * @inheritdoc
    * @private
    */
   beforeDestroy: function () {
       var me = this;
       me.years = me.months = null;
       Ext.destroyMembers(me, 'backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');
       me.callParent();
   },

   onDestroy: function () {
       Ext.destroyMembers(this, 'okBtn', 'cancelBtn');
       this.callParent();
   },

   privates: {
       // Do the job of a container layout at this point even though we are not a Container.
       // TODO: Refactor as a Container.
       finishRenderChildren: function () {
           var me = this;

           this.callParent(arguments);

           if (this.showButtons) {
               me.okBtn.finishRender();
               me.cancelBtn.finishRender();
           }
       }
   }
});

//只选择年的控件
Ext.define('Ext.ux.form.YearField', {
   extend: 'Ext.form.field.Date',
   alias: 'widget.yearfield',
   format: 'Y',
   selectYear: new Date(),
   createPicker: function () {
       var me = this;
       return new ux.picker.Year({
           value: new Date(),
           renderTo: document.body,
           floating: true,
           hidden: true,
           focusOnShow: true,
           listeners: {
               scope: me,
               select: me.onSelect,
               cancelclick: me.onCancelClick,
               okclick: me.onOKClick,
               yeardblclick: me.onOKClick,
               monthdblclick: me.onOKClick
           }
       });
   },
   onCancelClick: function () {
       var me = this;
       me.selectYear = null;
       me.collapse();
   },
   onOKClick: function () {
       var me = this;
       if (me.selectYear) {
           me.setValue(me.selectYear);
           me.fireEvent('select', me, me.selectYear);
       }
       me.collapse();
   },
   onSelect: function (m, d) {
       var me = this;
       me.selectYear = new Date((d[0] + 1) + '/1/' + d[1]);
   }
});